package cn.chrisn.stream;

import cn.chrisn.pojo.Author;
import cn.chrisn.pojo.Book;
import com.sun.media.sound.FFT;
import lombok.EqualsAndHashCode;

import javax.sound.sampled.EnumControl;
import java.lang.management.BufferPoolMXBean;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Description
 * @Author Chrisn
 * @Date 2025/4/26
 */
public class StreamTest {
    public static void main(String[] args) {
        parallelTest();
    }
    private static void parallelTest() {
        //1.法一
        Stream<Author> authorStream = getAuthors().parallelStream();
        System.out.println("=============");

        //2.法二
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        stream.parallel().peek(num-> System.out.println(num+Thread.currentThread().getId()+Thread.currentThread().getName())).filter(num->num>5).reduce((result, ele)->result+ele).ifPresent(System.out::println);
    }
    private static void toOtherTypeStreamTest() {
        //因为有的时候计算需要拆箱装箱，比如Integer类型转Int类型，所以函数式接口有一些已经定义好的接口提高了效率，直接用就行了
        getAuthors().stream().mapToInt(new ToIntFunction<Author>() {
            @Override
            public int applyAsInt(Author value) {
                return value.getAge()+10;
            }
        }).forEach(System.out::println);
    }

    private static void methodUseTest() {
        //使用前提
        //如果我们在重写方法的时候，方法体中只有一行代码，并且这行代码是调用了某个类的静态方法，并且我们把要重写的抽象方法中所有
        //的参数都按照顺序传入了这个静态方法中，这个时候我们就可以引用类的静态方法。
        getAuthors().stream().map(Author::getAge).map(String::valueOf).forEach(System.out::println);
        System.out.println("==============");
        //使用前提
        //如果我们在重写方法的时候，方法体中只有一行代码，并且这行代码是调用了某个对象的成员方法，并且我们把要重写的抽象方法中所
        //有的参数都按照顺序传入了这个成员方法中，这个时候我们就可以引|用对象的实例方法
        Stream<Author> stream = getAuthors().stream();
        StringBuilder sb = new StringBuilder();
        stream.map(author -> author.getName()).forEach(sb::append);
        System.out.println("==============");
        //使用前提
        //如果我们在重写方法的时候，方法体中只有一行代码，并且这行代码是**调用了第一个参数的成员方法**，并且我们把要重写的抽象方
        //法中剩余的所有的参数都按照顺序传入了这个成员方法中，这个时候我们就可以引用类的实例方法

        //6.3.5构造器引用
        //如果方法体中的一行代码是构造器的话就可以使用构造器引用
        //使用前提
        //如果我们在重写方法的时候，方法体中只有一行代码，并且这行代码是调用了某个类的构造方法，并且我们把要重写的抽象方法中的所
        //有的参数都按照顺序传入了这个构造方法中，这个时候我们就可以引|用构造器。
        List<Author> authors = getAuthors();
        authors.stream()
                .map(Author::getName)
                .map(StringBuilder::new)
                .map(sb1->sb1.append("-三更").toString())
                .forEach(System.out::println);
    }

    private static void negateTest() {
        getAuthors().stream().filter(new Predicate<Author>() {
            @Override
            public boolean test(Author author) {
                return author.getAge() > 17;
            }
        }.negate()).forEach(author -> System.out.println(author.getName() + author.getAge()));
        System.out.println("===========");
        getAuthors().stream().filter(author -> !(author.getAge() > 17)).forEach(author -> System.out.println(author.getName() + author.getAge()));
    }

    private static void orTest() {
        //打印作家中年龄大于17或者姓名的长度小于2的作家
        getAuthors().stream().filter(new Predicate<Author>() {
            @Override
            public boolean test(Author author) {
                return author.getAge() > 17;
            }
        }.or(new Predicate<Author>() {
            @Override
            public boolean test(Author author) {
                return author.getName().length() < 2;
            }
        })).forEach(author -> System.out.println(author.getName() + ":::" + author.getAge()));
        System.out.println("==============");
        getAuthors().stream().filter(author -> author.getAge() > 17 || author.getName().length() < 2).forEach(author -> System.out.println(author.getName() + ":::" + author.getAge()));
    }

    private static void andTest() {
        //应用场景是自己去定义函数式编程的时候
        getAuthors().stream().filter(((Predicate<Author>) author -> author.getAge() > 17).and(author -> author.getName().length() > 1)).forEach(author -> System.out.println(author.getName() + author.getAge()));
        //不然还不如用&&
        getAuthors().stream().filter(((Predicate<Author>) author -> author.getAge() > 17 && author.getName().length() > 1)).forEach(author -> System.out.println(author.getName() + author.getAge()));
    }

    private static void isPresentTest() {
        //4.2.6判断
        //我们可以使用isPresent方法进行是否存在数据的判断。如果为空返回值为false,如果不为空，返回值为true。但是这种方式并不能体现
        //Optional的好处，更推荐使用ifPresent方法
        if (createAuthorOptional().isPresent()) {
            System.out.println("正确执行");
        }
    }

    private static void filter() {
        //我们可以使用filter方法对数据进行过滤。如果原本是有数据的，但是不符合判断，也会变成一个无数据的Optional对象。
        createAuthorOptional().filter(author -> author.getAge() > 18).ifPresent(author -> System.out.println(author.getAge()));
    }

    private static void orElseThrowTest() {
        //orElseThrow
//获取数据，如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建异常抛出。
        Optional<Author> authorOptional = createAuthorOptional();
        try {
            Author author = authorOptional.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("数据为null"));
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    private static void unSafeGetValue() {
        //1.获取值，但是里面的值是空的时候会报NoSuchElementException
//        System.out.println(createAuthorOptional().get());
        //2. orElseGet
        //获取数据并且设置数据为空时的默认值。如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建对象作为默认值返回。
        Author author = createAuthorOptional().orElseGet(() -> new Author(120L, "xiaowlllll", 18, "情感", null));
        System.out.println(author);
    }

    private static void safeConsumer() {
        createAuthorOptional().ifPresent(author -> System.out.println(author));
    }

    private static Optional<Author> createAuthorOptional() {
        //1.创建optional ofNullabel可以为null
        Author author = new Author(100L, "chrisn", 18, "情感", null);

        //2.创建optional of不可以为null
//        return Optional.of(null);
        //3.emtpty()可以把null转化为Optional，ofNullable底层就是这样保证总是可以创建的 XXX==null？Optional.empty():Optional.of(author)
        Optional<Object> empty = Optional.empty();
        return Optional.ofNullable(author);
    }

    private static void reduceTest() {
        //对流中的数据按照你指定的计算方式计算出一个结果。 (缩减操作)1
        //reduce的作用是把stream中的元素给组合起来，我们可以传入一个初始值，它会按照我们的计算方式依次拿流中的元素和在初始化值
        //的基础上进行计算，计算结果再和后面的元素计算

        //例子：
        //使用reduce求所有作者年龄的和
        Integer sum = getAuthors().stream().distinct().map(author -> author.getAge()).reduce(0, (result, element) -> result + element);
        System.out.println(sum);
        //使用reduce求所有作者中年龄的最大值
        //这里我有个疑问哈？他是把流中的对象每一次都拿出来和result对比，result实际是最小值，元素大于最小值后就放回这个元素，那么本质上这个result是不变的，那么难道不会返回很多个元素嘛，怎么能拿到所有作者中的最大值呢
        /**
         * reduce(Integer.MIN_VALUE, new BinaryOperator<Integer>() {...}) ：该方法用于对流中的元素进行累积操作。
         * 初始值 Integer.MIN_VALUE ：提供了一个初始值作为累积操作的起始点。在不断比较过程中，这个初始值会与流中的元素进行比较，确保即使所有元素都小于或等于它，也能得到合理的最大值（虽然这种情况下最大值会是 Integer.MIN_VALUE，但正常情况下作者年龄不太可能这么小）。
         * BinaryOperator ：是一个函数式接口，用于定义累积操作的逻辑。在这里的实现中，如果当前元素 element 大于累积结果 result，则将 element 作为新的累积结果；否则，继续保留 result，从而逐步筛选出最大的年龄值。
         * 原来是这样，默认是缩减操作，他还是放回值作为了下个累计值，就相当于return的元素=下一次流的执行中新的result
         */
        Integer max = getAuthors().stream().distinct().map(author -> author.getAge()).reduce(Integer.MIN_VALUE, new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer result, Integer element) {
                return element > result ? element : result;
            }
        });
        System.out.println(max);
        //使用reduce求所有作者中年龄的最小值
        Integer min = getAuthors().stream().distinct().map(Author -> Author.getAge()).reduce(Integer.MAX_VALUE, new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer result, Integer element) {
                return result > element ? element : result;
            }
        });
        System.out.println(min);

        //默认是把第一个元素作为默认值
        /*
         boolean foundAny = false;
         T result = null;
         for (T element : this stream){
         if (!foundAny) {
         foundAny = true;
         result = element;
         } else result = accumulator.apply(result, element);
         }
         return foundAny ? Optional.of(result) : Optional.empty();
         */
        Optional<Integer> oneArgsMin = getAuthors().stream().distinct().map(author -> author.getAge()).reduce(new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer result, Integer element) {
                return result > element ? element : result;
            }
        });
        oneArgsMin.ifPresent(args -> System.out.println(args));
    }

    private static void findFirstTest() {
        //findFirst
        //获取流中的第一个元素。
        //例子：获取一个年龄最小的作家，并输出他的姓名。
        Optional<Author> first = getAuthors().stream().sorted(((o1, o2) -> o1.getAge() - o2.getAge())).findFirst();
        first.ifPresent(author -> System.out.println(author.getName() + author.getAge()));
    }

    private static void findAny() {
        //findAny
        //获取流中的任意一个元素。该方法没有办法保证获取的一定是流中的第一个元素。
        //例子：
        //获取任意一个大于18的作家，如果存在就输出他的名字
        Optional<Author> any = getAuthors().stream().filter(author -> author.getAge() > 18).findAny();
        any.ifPresent(author -> System.out.println(author.getName()));
    }

    private static void noneMatchTest() {
        //noneMatch
        //可以判断流中的元素是否都不符合匹配条件。如果都不符合结果为true，否则结果为false
        //例子：
        //判断作家是否都没有超过100岁的。
        boolean flags = getAuthors().stream().noneMatch(author -> author.getAge() > 100);
        System.out.println(flags);
    }

    private static void allMatch() {
        //allMatch
        //可以用来判断是否都符合匹配条件，结果为boolean类型。如果都符合结果为true，否则结果为false。
        //例子：
        //判断是否所有的作家都是成年人
        boolean flags = getAuthors().stream().allMatch(author -> author.getAge() >= 18);
        System.out.println(flags);
    }

    private static void anyMathchTest() {
        //anyMatch
        //可以用来判断是否有任意符合匹配条件的元素，结果为boolean类型。
        //例子：
        //判断是否有年龄在29以上的作家
        boolean flags = getAuthors().stream().anyMatch(new Predicate<Author>() {
            @Override
            public boolean test(Author author) {
                return author.getAge() > 29;
            }
        });
        System.out.println(flags);
    }

    private static void CollectionAndMapTest() {
        //collect
        //把当前流转换成一个集合。
        //collect就不要用匿名内部类的形式了，里面提供了一个Collections的工具类，里面的toList()方法可以转换成集合
        //例子:
        //获取一个存放所有作者名字的List集合。
        List<String> nameList = getAuthors().stream().map(author -> author.getName()).distinct().collect(Collectors.toList());
        System.out.println(nameList);
        //获取一个所有书名的Set集合。
        Set<String> BookNameSet = getAuthors().stream().flatMap(author -> author.getBooks().stream()).map(book -> book.getName()).distinct().collect(Collectors.toSet());
        System.out.println(BookNameSet);
        //获取一个map集合，map的key为作者名，value为List<Book>
        Map<String, List<Book>> collect = getAuthors().stream().distinct().collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
        for (String key : collect.keySet()) {
            System.out.println("key是：" + key);
        }
        for (List<Book> value : collect.values()) {
            System.out.println("value是:" + value);
        }
        System.out.println("==============");
        for (Map.Entry<String, List<Book>> map : collect.entrySet()) {
            System.out.println("key是：" + map.getKey() + "value是:" + map.getValue());
        }
    }

    private static void maxAndMinTest() {
        //max&min
        //可以用来或者流中的最值。
        //例子：
        //分别获取这些作家的所出书籍的最高分和最低分并打印
        Optional<Integer> max = getAuthors().stream().flatMap(author -> author.getBooks().stream()).map(book -> book.getScore()).max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        System.out.println(max.get());
        System.out.println("============");
        Optional<Integer> min = getAuthors().stream().flatMap(author -> author.getBooks().stream()).map(book -> book.getScore()).min(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        System.out.println(min.get());
        //ps：注意一个流对象终结后就不能再用了，你需要后续的操作就重新定义一个流
    }

    private static void countTest() {
        //count
        //可以用来获取当前流中元素的个数。
        //例子:
        //打印这些作家的所出书籍的数目，注意删除重复元素。
        long count = getAuthors().stream().flatMap(author -> author.getBooks().stream()).distinct().count();
        System.out.println(count);
    }

    private static void forEachTest() {
        //对流中的元素进行遍历操作，我们通过传入的参数去指定对遍历到的元素进行什么具体操作。
        //例子：
        //输出所有作家的名字
        getAuthors().stream().map(author -> author.getName()).distinct().forEach(name -> System.out.println(name));
        System.out.println("=================");
        getAuthors().stream().distinct().forEach(author -> System.out.println(author.getName()));
    }

    private static void flatMapTest() {
        //map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素。
        //1.打印所有书籍的名字。要求对重复的元素进行去重。
        getAuthors().stream().flatMap(new Function<Author, Stream<Book>>() {
            @Override
            public Stream<Book> apply(Author author) {
                return author.getBooks().stream();
            }
        }).distinct().forEach(new Consumer<Book>() {
            @Override
            public void accept(Book book) {
                System.out.println(book);
            }
        });
        //2.打印现有数据的所有分类。要求对分类进行去重。不能出现这种格式：哲学，爱情
        getAuthors().stream().flatMap(author -> author.getBooks().stream()).distinct().flatMap(book -> Arrays.stream(book.getCategory().split("，"))).distinct().forEach(category -> System.out.println(category));
    }

    private static void skipTest() {
        //skip跳过流中的前n个元素，返回剩下的元素
        //打印除了年龄最大的作家外的其他作家，要求不能有重复元素，并且按照年龄降序排序。
        getAuthors().stream().distinct().sorted(((o1, o2) -> o2.getAge() - o1.getAge())).skip(1).forEach(author -> System.out.println(author.getName() + author.getAge()));
    }

    private static void limit() {
        //limit设计流的最大长度，超出的部分将被抛弃
        //对流中的元素按照年龄进行降序排序，并且要求不能有重复的元素，然后打印其中年龄最大的两个作家的姓名
        getAuthors().stream().distinct().sorted(((o1, o2) -> o2.getAge() - o1.getAge())).limit(2).forEach(author -> System.out.println(author.getName() + author.getAge()));
    }

    private static void sortedTest() {
        //对流中的元素按照年龄进行降序排序，并且要求不能有重复的元素。
        //1.空参sorted方法，需要流中的元素实现Comparable<>,比较器是用正负来确定正负;
        List<Author> authors = getAuthors();
        authors.stream().distinct().sorted().forEach(author -> System.out.println(author.getName() + author.getAge()));
        //2.有参的方式
        System.out.println("===================");
        authors.stream().distinct().sorted((o1, o2) -> o1.getAge() - o2.getAge()).forEach(author -> System.out.println(author.getName() + author.getAge()));
    }

    private static void distinctTest() {
        //默认distinct依赖与Object类的equals方法，所以注意重写equals方法
        //object类的equals方法是默认使用地址进行比对，一致的就是相同对象
        //但是我们自己定义的类比如Authors类，需要重写，因为我们默认属性值相同的为一个类
        //那么说，用地址显然不符合嘛，所以要重写equals方法
        //@EqualsAndHashCode//用于后续的去重使用
        /*
         等同于
         @Override
         public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         Author author = (Author) o;
         return Objects.equals(id, author.id) && Objects.equals(name, author.name) && Objects.equals(age, author.age) && Objects.equals(intro, author.intro) && Objects.equals(books, author.books);
         }
         @Override
         public int hashCode() {
         return Objects.hash(id, name, age, intro, books);
         }
         */
        getAuthors().stream().distinct().forEach(author -> System.out.println(author.getName()));
    }

    private static void mapTest() {
        //map 主要用于流内的元素进行计算和转换
        //打印所有作家的姓名
        List<Author> authors = getAuthors();
        // Function源码可知，两个泛型，第一个是传入参数，第二个是返回类型
        //1.转化
        authors.stream().distinct().map(author -> author.getName()).forEach(name -> System.out.println(name));
        //2.计算
        authors.stream().distinct().map(author -> author.getAge()).map(age -> age + 10).forEach(age -> System.out.println(age));
    }

    private static void filterTest() {
        //filter主要用于流内元素的过滤
        List<Author> authors = getAuthors();
        getAuthors().stream().filter(author -> author.getName().length() > 1).forEach(author -> System.out.println(author.toString()));
    }

    private static void mapCreateStreamWays() {
        //双列集合转化成流
        //1.双列集合->单列集合
        Map<String, Integer> map = new HashMap<>();
        map.put("chrisn", 21);
        map.put("tyre", 20);
        map.put("xiaojie", 11);
        map.put("xiaowang", 45);
        //2.创建流
        Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();
        stream.filter(new Predicate<Map.Entry<String, Integer>>() {
            @Override
            public boolean test(Map.Entry<String, Integer> stringIntegerEntry) {
                return stringIntegerEntry.getValue() > 21;
            }
        }).forEach(new Consumer<Map.Entry<String, Integer>>() {
            @Override
            public void accept(Map.Entry<String, Integer> stringIntegerEntry) {
                System.out.println(stringIntegerEntry.getKey() + "===" + stringIntegerEntry.getValue());
            }
        });
    }

    private static void arrCreateStreamWays() {
        //2.数组创造流对象
        //使用Arrays里的stream方法
        Integer[] arr = {1, 2, 3, 4, 5, 1, 34, 34, 44, 45, 6, 2, 3, 4, 5};
        Stream<Integer> streamArr = Arrays.stream(arr);
        streamArr.distinct()
                .filter(integer -> integer > 5)
                .forEach(integer -> {
                    System.out.println(integer);
                });
        //使用Stream.of()方法创建
        Stream<Integer> arrByStream = Stream.of(arr);
        arrByStream.distinct()
                .filter(integer -> integer < 5)
                .forEach(integer -> {
                    System.out.println(integer);
                });
    }

    private static void collectionCreateStreamWays() {
        List<Author> authors = getAuthors();
//        //1.单列集合创建流对象
//        Stream<Author> stream = authors.stream();
        //1.把集合转换成流
        authors.stream()
                //去重
                .distinct()
                //过滤
                .filter(author -> author.getAge() < 18)
                .forEach(author -> System.out.println(author.getName()));
    }

    private static List<Author> getAuthors() {
        //数据初始化
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
        Author author2 = new Author(2L, "亚拉索", 15, "狂风也追逐不上他的思考速度", null);
        Author author3 = new Author(3L, "易", 14, "是这个世界在限制他的思维", null);
        Author author4 = new Author(3L, "易", 14, "是这个世界在限制他的思维", null);
        //书籍列表
        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();
        books1.add(new Book(1L, "刀的两侧是光明与黑暗", "哲学，爱情", 88, "用一把刀划分了爱恨"));
        books1.add(new Book(2L, "一个人不能死在同一把刀下", "个人成长，爱情", 99, "讲述如何从失败中明悟真理"));
        books2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new Book(4L, "吹或不吹", "爱情，个人传记", 56, "一个哲学家的恋爱观注定很难把他所在的时代理解"));
        books3.add(new Book(5L, "你的剑就是我的剑", "爱情", 56, "无法想象一个武者能对他的伴侣这么的宽容"));
        books3.add(new Book(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢？"));
        books3.add(new Book(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢？"));

        author.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);

        List<Author> authorList = new ArrayList<>(Arrays.asList(author, author2, author3, author4));
        return authorList;
    }
}
